﻿using System.Collections.Generic;
using UIOC.Attributes;
using Unity.VisualScripting.YamlDotNet.Core.Tokens;

namespace UIOC.Observable {

    /// <summary>
    /// 可观察数据
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    public interface IObservableData<T> : IObservableObject {

        /// <summary>
        /// 当值发生改变时
        /// </summary>
        /// <param name="oldValue">更新前的值</param>
        /// <param name="newValue">更新后的值</param>
        public delegate void ValueChangedDelegate(T oldValue, T newValue);

        /// <summary>
        /// 当值发生改变时
        /// </summary>
        event ValueChangedDelegate ValueChanged;

        /// <summary>
        /// 数据值
        /// </summary>
        T Value { get; }

        void ClearValueChanged();
    }

    /// <summary>
    /// 数据
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    public interface IData<T> : IObject, IObservableData<T> {

        /// <summary>
        /// 数据值
        /// </summary>
        new T Value { get; set; }

        void SetValueWithoutNotify(T value);

        void SetValueAndNotify(T value);
    }

    [Implement(typeof(IObservableData<>))]
    [Implement(typeof(IData<>))]
    public partial class Data<T> : Object, IData<T> {

        public virtual event IObservableData<T>.ValueChangedDelegate ValueChanged;

        private T m_Value;

        protected override void Release() {
            ValueChanged = null;
            m_Value = default;
            base.Release();
        }

        protected override void Dispose() {
            ValueChanged = null;
            m_Value = default;
            base.Dispose();
        }

        public virtual void ClearValueChanged() {
            ValueChanged = null;
        }

        protected virtual T GetValue() => m_Value;

        public virtual T Value {
            get => m_Value;
            set => SetValue(value);
        }

        protected virtual void SetValue(T value) {
            if (!EqualsValue(value)) {
                SetValueAndNotify(value);
            }
        }

        protected virtual void NotifyValueChanged(T oldValue, T newValue) {
            ValueChanged?.Invoke(oldValue, newValue);
            NotifyChanged();
        }

        void IData<T>.SetValueWithoutNotify(T value) {
            SetValueWithoutNotify(value);
        }
        protected virtual void SetValueWithoutNotify(T value) {
            if (!EqualsValue(value)) {
                DoSetValue(value);
            }
        }

        void IData<T>.SetValueAndNotify(T value) {
            SetValueAndNotify(value);
        }
        protected virtual void SetValueAndNotify(T value) {
            var oldValue = m_Value;
            DoSetValue(value);
            NotifyValueChanged(oldValue, value);
        }

        protected virtual void DoSetValue(T value) {
            m_Value = value;
        }

        protected virtual bool EqualsValue(T @in) {
            return EqualityComparer<T>.Default.Equals(@in, m_Value);
        }

        public override string ToString() {
            return m_Value?.ToString() ?? "null";
        }
    }
}